package org.openmore.platform.pay.alipay;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import io.swagger.annotations.ApiParam;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.openmore.common.enums.PaymentType;
import org.openmore.common.exception.OpenmoreException;
import org.openmore.platform.dto.common.BaseResponse;
import org.openmore.platform.entity.PlatformConfig;
import org.openmore.platform.entity.enums.PlatformType;
import org.openmore.platform.pay.Payment;
import org.openmore.platform.pay.PaymentService;
import org.openmore.platform.service.PlatformConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;

import javax.servlet.http.HttpServletRequest;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

@Service
public class AliPayService implements PaymentService {

    private static Map<String, MyAliPayClient> alipayClientMap;

    private Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    private PlatformConfigService platformConfigService;

    private Map<String, MyAliPayClient> getInstance(){
        if (alipayClientMap == null) {
            alipayClientMap = new HashMap<>();
        }
        return alipayClientMap;
    }

    public MyAliPayClient getAlipayClient(String tenantId){
        if (StringUtils.isEmpty(tenantId)) {
            throw new OpenmoreException("租户id不能为空");
        }
        Map<String, MyAliPayClient> alipayCliens = getInstance();
        MyAliPayClient alipayClient = alipayCliens.get(PaymentType.ALIPAY + tenantId);
        if(null == alipayClient){
            List<PlatformConfig> platformConfigs = platformConfigService.selectAll(tenantId, PlatformType.ALIPAY, null, null);
            PlatformConfig aliPayConfig = platformConfigs.get(0);
            if (aliPayConfig == null) {
                throw new OpenmoreException("找不到应用的支付宝平台配置信息");
            }
            alipayClient = new MyAliPayClient(
                    "https://openapi.alipay.com/gateway.do",//TODO ::server_url
                    aliPayConfig.getAppid(),
                    aliPayConfig.getPrivateKey(),//privateKey
                    "json",
                    "utf-8",
                    aliPayConfig.getPublicKey(),
                    "RSA2"
            );
            alipayClientMap.put(PaymentType.ALIPAY + tenantId, alipayClient);
        }
        return alipayClient;
    }


    public BaseResponse payment(String host, String tenantId, Payment payment) throws AlipayApiException {
        if(payment.getPayType() != PaymentType.ALIPAY){
            throw new OpenmoreException(400, "支付类型非法");
        }
        AlipayClient client = getAlipayClient(tenantId);
        //实例化具体API对应的request类,
        // 类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        //SDK已经封装掉了公共参数，这里只需要传入业务参数。
        // 以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setBody(payment.getBody());
        model.setSubject(payment.getBody());
        Double fee = (double) payment.getTotal_fee() / 100;
        DecimalFormat df = new DecimalFormat("#.##");
        String feeString = df.format(fee);
        model.setOutTradeNo(payment.getOut_trade_no());
        model.setTimeoutExpress("30m");
        model.setTotalAmount(feeString);
        model.setProductCode("QUICK_MSECURITY_PAY");
        request.setBizModel(model);
        //TODO :: http or https
        String notifyUrl = "http://"+host+"/api/payNotify/" + tenantId + "/alipay/callback";
        request.setNotifyUrl(notifyUrl);
        //这里和普通的接口调用不同，使用的是sdkExecute
        AlipayTradeAppPayResponse response = client.sdkExecute(request);
        return BaseResponse.buildSuccessResponse(response.getBody());
    }

    public BaseResponse paynotify(HttpServletRequest request,
                     @PathVariable @ApiParam(name = "应用ID")String tenantId){
        Map<String, String> params = this.getResultParams(request.getParameterMap());
        boolean isSign = false;
        MyAliPayClient myAliPayClient = getAlipayClient(tenantId);
        try {
            isSign = AlipaySignature.rsaCheckV1(params,myAliPayClient.getAliPayConfig().getPublicKey(),"utf-8","RSA2");
        }catch (AlipayApiException e){
            e.printStackTrace();
            //return "failure";
            return BaseResponse.buildSuccessResponse(false);
        }

        float total_amount_float = Float.parseFloat(params.get("total_amount"))*100;
        Long payPrice = Long.parseLong(String.valueOf((int)total_amount_float));
        //判断成功之后使用getResponse方法判断是否是支付宝发来的异步通知。
        if (AlipayNotify.verifyResponse(myAliPayClient.getAliPayConfig().getAppid(),params.get("notify_id")).equals("true")){
            if (isSign){
                /*if (params.get("trade_status").equals("TRADE_FINISHED") || params.get("trade_status").equals("TRADE_SUCCESS")) {
                    commonOrderService.afterPayPayment(params.get("out_trade_no"), null, params.get("trade_no"), payPrice);
                }*/
                //return "success";
                return BaseResponse.buildSuccessResponse(true);
            } else
                //return "failure";
                return BaseResponse.buildSuccessResponse(false);
        } else{
            //return "failure";
            return BaseResponse.buildSuccessResponse(false);
        }
    }

    /***校验签名**/
    public boolean verifySign(String tenantId, HttpServletRequest request){
        //获取支付宝POST过来反馈信息
        Map<String,String> params = new HashMap<String,String>();
        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        //切记alipaypublickey是支付宝的公钥，请去open.alipay.com对应应用下查看。
        //boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
        boolean flag = false;
        try {
            MyAliPayClient myAliPayClient = alipayClientMap.get(PaymentType.ALIPAY + tenantId);
            flag = AlipaySignature.rsaCheckV1(params, myAliPayClient.getAliPayConfig().getPublicKey(), "utf-8","RSA2");
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new OpenmoreException(400, "阿里支付通知验证签名出错");
        }
        return flag;
    }

    private Map<String, String> getResultParams(Map requestParams){
        Map<String, String> params = new HashMap<>();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
            params.put(name, valueStr);
        }
        return params;
    }
}
